package ORDB4ORA.util;

import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;



public class derivedAttributeAdapter extends AdapterImpl {
	private final EObjectImpl source;
	private final int derivedFeature;

	private List<Integer> localFeatures = new ArrayList<Integer>();

	// TODO this lot could be put into a subclass and put in a list to allow for
	// multiple navigated dependencies
	private int dependantFeature = Notification.NO_FEATURE_ID;
	private Class<?> dependantClass = null;
	private int navigationFeature = Notification.NO_FEATURE_ID;

	private AdapterImpl dependantAdapter = new AdapterImpl() {

		@Override
		public void notifyChanged(Notification msg) {

			if (msg.getFeatureID(dependantClass) == dependantFeature
					&& msg.getEventType() == Notification.SET) {
				notifyDerivedAttributeChange();
			}
		}
	};

	/*
	 * Convenience constructor for a local and navigated dependency
	 */
	public derivedAttributeAdapter(EObjectImpl source, int derivedFeature,
			int navigationFeature, int dependantFeature, int localFeature) {
		this(source, derivedFeature);
		addNavigatedDependency(navigationFeature, dependantFeature);
		addLocalDependency(localFeature);
	}

	/*
	 * Convenience constructor for a navigated dependency
	 */
	public derivedAttributeAdapter(EObjectImpl source, int derivedFeature,
			int navigationFeature, int dependantFeature) {
		this(source, derivedFeature);
		addNavigatedDependency(navigationFeature, dependantFeature);
	}

	/*
	 * Convenience constructor for a local dependency
	 */
	public derivedAttributeAdapter(EObjectImpl source, int derivedFeature,
			int localFeature) {
		this(source, derivedFeature);
		addLocalDependency(localFeature);
	}

	public derivedAttributeAdapter(EObjectImpl source, int derivedFeature) {
		super();
		this.source = source;
		this.derivedFeature = derivedFeature;
		source.eAdapters().add(this);
	}

	public void addNavigatedDependency(int navigationFeature,
			int dependantFeature) {
		this.dependantFeature = dependantFeature;
		this.navigationFeature = navigationFeature;
		this.dependantClass = source.eClass().getEStructuralFeature(
				navigationFeature).getEType().getInstanceClass();

	}

	public void addLocalDependency(int localFeature) {
		localFeatures.add(localFeature);
	}

	@Override
	public void notifyChanged(Notification notification) {
		if (notification.getFeatureID(source.getClass()) == navigationFeature) {
			switch (notification.getEventType()) {
			// TODO support ADD_MANY/REMOVE_MANY?
			case Notification.ADD:
				EObject added = (EObject) notification.getNewValue();
				added.eAdapters().add(dependantAdapter);
				break;
			case Notification.SET:
				EObject newValue = (EObject) notification.getNewValue();
				EObject oldValue = (EObject) notification.getOldValue();
				if (oldValue != null)
					oldValue.eAdapters().remove(dependantAdapter);
				if (newValue != null)
					newValue.eAdapters().add(dependantAdapter);
				break;
			case Notification.REMOVE:
				EObject removed = (EObject) notification.getOldValue();
				removed.eAdapters().remove(dependantAdapter);
				break;
			default:
				return; // No notification
			}
			notifyDerivedAttributeChange();
		} else if (localFeatures.contains(notification.getFeatureID(source
				.getClass()))) {
			if (notification.getEventType() == Notification.SET) {
				notifyDerivedAttributeChange();
			}
		}
	}

	private void notifyDerivedAttributeChange() {
		if (source.eNotificationRequired()) {
			source.eNotify(new ENotificationImpl(source, Notification.SET,
					derivedFeature, null, source.eGet(derivedFeature, true,
							true)));
		}
	}

}